
package com.joolun.cloud.weixin.admin.controller;

import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.joolun.cloud.common.core.constant.CommonConstants;
import com.joolun.cloud.common.core.util.R;
import com.joolun.cloud.common.log.annotation.SysLog;
import com.joolun.cloud.common.security.annotation.Inside;
import com.joolun.cloud.weixin.admin.config.ma.WxMaConfiguration;
import com.joolun.cloud.weixin.admin.config.mp.WxMpConfiguration;
import com.joolun.cloud.weixin.admin.config.open.WxOpenConfiguration;
import com.joolun.cloud.weixin.admin.service.WxAppService;
import com.joolun.cloud.weixin.common.constant.ConfigConstant;
import com.joolun.cloud.weixin.common.constant.WxReturnCode;
import com.joolun.cloud.weixin.common.dto.LoginDTO;
import com.joolun.cloud.weixin.common.dto.WxOpenDataDTO;
import com.joolun.cloud.weixin.common.entity.WxApp;
import com.joolun.cloud.weixin.common.entity.WxUser;
import com.joolun.cloud.weixin.admin.service.WxUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.service.WxOAuth2Service;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.open.api.impl.WxOpenOAuth2ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;

/**
 * 微信用户
 *
 * @author www.joolun.com
 * @date 2019-03-25 15:39:39
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/wxuser")
@Api(value = "wxuser", tags = "微信用户管理")
public class WxUserController {

	private final WxUserService wxUserService;
	private final WxAppService wxAppService;

	/**
	* 分页查询
	* @param page 分页对象
	* @param wxUser 微信用户
	* @return
	*/
	@ApiOperation(value = "分页查询")
	@GetMapping("/page")
	@PreAuthorize("@ato.hasAuthority('wxmp:wxuser:index')")
	public R getWxUserPage(Page page, WxUser wxUser,String tagId) {
		Wrapper<WxUser> queryWrapper;
		if(StringUtils.isNotBlank(tagId)){
			queryWrapper = Wrappers.lambdaQuery(wxUser)
					.and(wrapper -> wrapper
							.eq(WxUser::getTagidList,"["+tagId+"]")
							.or()
							.like(WxUser::getTagidList,","+tagId+",")
							.or()
							.likeRight(WxUser::getTagidList,"["+tagId+",")
							.or()
							.likeLeft(WxUser::getTagidList,","+tagId+"]"));
		}else if(StrUtil.isNotBlank(wxUser.getNickName())){
			String nickName = wxUser.getNickName();
			wxUser.setNickName(null);
			queryWrapper = Wrappers.lambdaQuery(wxUser)
					.like(WxUser::getNickName,nickName);
		}else{
			queryWrapper = Wrappers.lambdaQuery(wxUser);
		}
		return R.ok(wxUserService.page(page,queryWrapper));
	}


	/**
	* 通过id查询微信用户
	* @param id id
	* @return R
	*/
	@ApiOperation(value = "通过id查询微信用户")
	@GetMapping("/{id}")
	@PreAuthorize("@ato.hasAuthority('wxmp:wxuser:get')")
	public R getById(@PathVariable("id") String id){
	return R.ok(wxUserService.getById(id));
	}

	/**
	* 新增微信用户
	* @param wxUser 微信用户
	* @return R
	*/
	@ApiOperation(value = "新增微信用户")
	@SysLog("新增微信用户")
	@PostMapping
	@PreAuthorize("@ato.hasAuthority('wxmp:wxuser:add')")
	public R save(@RequestBody WxUser wxUser){
	return R.ok(wxUserService.save(wxUser));
	}

	/**
	* 修改微信用户
	* @param wxUser 微信用户
	* @return R
	*/
	@ApiOperation(value = "修改微信用户")
	@SysLog("修改微信用户")
	@PutMapping
	@PreAuthorize("@ato.hasAuthority('wxmp:wxuser:edit')")
	public R updateById(@RequestBody WxUser wxUser){
	return R.ok(wxUserService.updateById(wxUser));
	}

	/**
	* 通过id删除微信用户
	* @param id id
	* @return R
	*/
	@ApiOperation(value = "通过id删除微信用户")
	@SysLog("删除微信用户")
	@DeleteMapping("/{id}")
	@PreAuthorize("@ato.hasAuthority('wxmp:wxuser:del')")
	public R removeById(@PathVariable String id){
    return R.ok(wxUserService.removeById(id));
  }

	@ApiOperation(value = "同步微信用户")
	@SysLog("同步微信用户")
	@PostMapping("/synchron")
	@PreAuthorize("@ato.hasAuthority('wxmp:wxuser:synchro')")
	public R synchron(@RequestBody WxUser wxUser){
		try {
			wxUserService.synchroWxUser(wxUser.getAppId());
			return new R<>();
		} catch (WxErrorException e) {
			e.printStackTrace();
			log.error("同步微信用户失败", e);
			return WxReturnCode.wxErrorExceptionHandler(e);
		}
	}

	/**
	 * 修改微信用户备注
	 * @param wxUser
	 * @return
	 */
	@ApiOperation(value = "修改微信用户备注")
	@SysLog("修改微信用户备注")
	@PutMapping("/remark")
	@PreAuthorize("@ato.hasAuthority('wxmp:wxuser:edit:remark')")
	public R remark(@RequestBody WxUser wxUser){
		try {
			return R.ok(wxUserService.updateRemark(wxUser));
		} catch (WxErrorException e) {
			e.printStackTrace();
			log.error("修改微信用户备注失败", e);
			return WxReturnCode.wxErrorExceptionHandler(e);
		}
	}

	/**
	 * 打标签
	 * @param data
	 * @return
	 */
	@ApiOperation(value = "打标签")
	@PutMapping("/tagid-list")
	@PreAuthorize("@ato.hasAuthority('wxmp:wxuser:tagging')")
	public R tagidList(@RequestBody JSONObject data){
		try {
			String appId = data.getStr("appId");
			String taggingType = data.getStr("taggingType");
			JSONArray tagIdsArray = data.getJSONArray("tagIds");
			JSONArray openIdsArray = data.getJSONArray("openIds");
			String[] openIds = openIdsArray.toArray(new String[0]);
			for(Object tagId : tagIdsArray){
				wxUserService.tagging(taggingType,appId,Long.valueOf(String.valueOf(tagId)),openIds);
			}
			return new R<>();
		} catch (WxErrorException e) {
			e.printStackTrace();
			log.error("修改微信用户备注失败", e);
			return WxReturnCode.wxErrorExceptionHandler(e);
		}
	}

	/**
	 * 小程序获取登录后的微信用户
	 * @param loginDTO
	 * @return R
	 */
	@ApiOperation(value = "小程序获取登录后的微信用户")
	@Inside
	@PostMapping("/inside/ma/code2wxuser")
	public R code2WxUserMa(@RequestBody LoginDTO loginDTO){
		WxApp wxApp = wxAppService.findByAppId(loginDTO.getAppId());
		if(wxApp == null){
			return R.failed("系统内无此小程序：" + loginDTO.getAppId() + "；请在后台添加");
		}
		try {
			WxMaJscode2SessionResult jscode2session = WxMaConfiguration.getMaService(wxApp.getId()).jsCode2SessionInfo(loginDTO.getJsCode());
			String openId = jscode2session.getOpenid();
			WxUser wxUser = wxUserService.getByOpenId(wxApp.getId(),openId);
			if(wxUser == null){
				wxUser = new WxUser();
				wxUser.setOpenId(openId);
			}else{
				//如果之前加入的时候没有入unionId，此次则更新
				if(StrUtil.isBlank(wxUser.getUnionId())){
					if(StrUtil.isNotBlank(jscode2session.getUnionid())){
						wxUser.setUnionId(jscode2session.getUnionid());
						wxUserService.updateById(wxUser);
					}
				}else{
					if(StrUtil.isNotBlank(jscode2session.getUnionid())
							&& !wxUser.getUnionId().equals(jscode2session.getUnionid())){
						wxUser.setUnionId(jscode2session.getUnionid());
						wxUserService.updateById(wxUser);
					}
				}
			}
			wxUser.setSessionKey(jscode2session.getSessionKey());
			wxUser.setUnionId(jscode2session.getUnionid());
			return R.ok(wxUser);
		} catch (WxErrorException e) {
			e.printStackTrace();
			return R.failed("小程序获取登录后的微信用户失败：" + loginDTO.getAppId());
		}
	}

	/**
	 * 公众号登录获取WxMpUser
	 * @param loginDTO
	 * @return R
	 */
	@ApiOperation(value = "公众号登录获取WxMpUser")
	@Inside
	@PostMapping("/inside/mp/code2wxuser")
	public R code2WxUserMp(@RequestBody LoginDTO loginDTO){
		WxApp wxApp = wxAppService.findByAppId(loginDTO.getAppId());
		if(wxApp == null){
			return R.failed("系统内无此公众号：" + loginDTO.getAppId() + "；请在后台添加");
		}
		try {
			WxMpService wxMpService = WxMpConfiguration.getMpService(wxApp.getId());
			WxOAuth2Service wxOAuth2Service = wxMpService.getOAuth2Service();
			WxOAuth2AccessToken wxOAuth2AccessToken;
			if(CommonConstants.YES.equals(wxApp.getIsComponent())){//第三方授权账号
				wxOAuth2AccessToken = WxOpenConfiguration.getOpenService().getWxOpenComponentService().getWxMpServiceByAppid(wxApp.getId()).getOAuth2Service().getAccessToken(loginDTO.getJsCode());
			}else{
				wxOAuth2AccessToken = wxOAuth2Service.getAccessToken(loginDTO.getJsCode());
			}
			String openId = wxOAuth2AccessToken.getOpenId();
			WxUser wxUser = wxUserService.getByOpenId(wxApp.getId(),openId);
			if(wxUser == null){
				wxUser = new WxUser();
				wxUser.setOpenId(openId);
			}
			if("snsapi_userinfo".equals(loginDTO.getScope())){
				WxOAuth2UserInfo wxMpUser = wxOAuth2Service.getUserInfo(wxOAuth2AccessToken,null);
				BeanUtil.copyProperties(wxMpUser,wxUser);
				wxUser.setHeadimgUrl(wxMpUser.getHeadImgUrl());
				wxUser.setNickName(wxMpUser.getNickname());
				wxUser.setUpdateTime(LocalDateTime.now());
				wxUserService.saveOrUpdate(wxUser);
			}
			return R.ok(wxUser);
		} catch (WxErrorException e) {
			e.printStackTrace();
			return R.failed("公众号登录获取WxMpUser失败：" + loginDTO.getAppId());
		}
	}

	/**
	 * 通过id查询微信用户
	 * @param id id
	 * @return R
	 */
	@ApiOperation(value = "通过id查询微信用户")
	@Inside
	@GetMapping("/inside/{id}")
	public R getByIdInside(@PathVariable("id") String id){
		return R.ok(wxUserService.getById(id));
	}

	/**
	 * 通过appType+union_id查询微信用户
	 * @param unionId
	 * @param appType
	 * @return R
	 */
	@ApiOperation(value = "通过appType+union_id查询微信用户")
	@Inside
	@GetMapping("/inside/by/unionid/{appType}/{unionId}")
	public R getByUnionIdInside(@PathVariable("unionId") String unionId, @PathVariable("appType") String appType){
		WxUser wxUser = wxUserService.getOne(Wrappers.<WxUser>lambdaQuery()
				.eq(WxUser::getUnionId, unionId)
				.eq(WxUser::getAppType, appType));
		return R.ok(wxUser);
	}

	/**
	 * 通过小程序授权更新微信用户
	 * @param wxOpenDataDTO
	 * @return R
	 */
	@ApiOperation(value = "通过小程序授权更新微信用户")
	@Inside
	@PutMapping("/ma/inside")
	public R updateByMaInside(@RequestBody WxOpenDataDTO wxOpenDataDTO){
		return R.ok(wxUserService.updateByMaInside(wxOpenDataDTO));
	}

	/**
	 * 通过公众号授权更新微信用户
	 * @param wxOpenDataDTO
	 * @return R
	 */
	@ApiOperation(value = "通过公众号授权更新微信用户")
	@Inside
	@PutMapping("/mp/inside")
	public R updateByMpInside(@RequestBody WxOpenDataDTO wxOpenDataDTO){
		return R.ok(wxUserService.updateByMpInside(wxOpenDataDTO));
	}

	/**
	 * 保存微信用户
	 * @param wxUser
	 * @return R
	 */
	@ApiOperation(value = "保存微信用户")
	@Inside
	@PostMapping("/inside")
	public R saveOrUpdateInside(@RequestBody WxUser wxUser){
		try{
			wxUserService.saveOrUpdate(wxUser);
		}catch (DuplicateKeyException e){
			if(e.getMessage().contains("uk_appid_openid")){
				return R.ok(wxUserService.getByOpenId(wxUser.getAppId(),wxUser.getOpenId()));
			}
		}

		if("".equals(wxUser.getMallUserId()) && StrUtil.isNotBlank(wxUser.getId())){
			if(StrUtil.isBlank(wxUser.getUnionId())){
				//没有UnionId，直接解绑退出商城登录
				wxUserService.update(
						new UpdateWrapper<WxUser>().lambda()
								.set(WxUser::getMallUserId,null)
								.eq(WxUser::getId,wxUser.getId())
				);
			}else{
				//有UnionId，解绑该unionId的所有用户
				wxUserService.update(
						new UpdateWrapper<WxUser>().lambda()
								.set(WxUser::getMallUserId,null)
								.eq(WxUser::getUnionId,wxUser.getUnionId())
				);
			}
		}
		return R.ok(wxUser);
	}

	/**
	 * 解密用户手机号信息.
	 * @param wxOpenDataDTO
	 * @return R
	 */
	@ApiOperation(value = "解密用户手机号信息")
	@Inside
	@PostMapping("/ma/inside/phoneinfo")
	public R getPhoneNoInfo(@RequestBody WxOpenDataDTO wxOpenDataDTO){
		WxMaUserService wxMaUserService = WxMaConfiguration.getMaService(wxOpenDataDTO.getAppId()).getUserService();
		WxMaPhoneNumberInfo wxMaPhoneNumberInfo = wxMaUserService.getPhoneNoInfo(wxOpenDataDTO.getSessionKey(), wxOpenDataDTO.getEncryptedData(), wxOpenDataDTO.getIv());
		return R.ok(wxMaPhoneNumberInfo);
	}
}
